package com.snail.workflow.utils;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.snail.common.core.utils.StringUtils;
import com.snail.workflow.api.constant.WorkflowConstants;
import com.snail.workflow.exception.WorkflowException;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.task.api.Task;
import org.springframework.expression.Expression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 流程工具类
 * @Author: Snail
 * @CreateDate: 2022/7/30 15:44
 * @Version: V1.0
 */
public class WorkflowUtils {

    /**
     * 校验目标节点是否是结束节点
     *
     * @param targetKey 目标节点
     * @param bpmnModel bpmnModel
     * @return
     */
    public static boolean checkIsEndNode(String targetKey, BpmnModel bpmnModel) {
        Process mainProcess = bpmnModel.getMainProcess();
        List<EndEvent> elements = mainProcess.findFlowElementsOfType(EndEvent.class, false);
        return elements.stream().anyMatch(item -> item.getId().equals(targetKey));
    }


    /**
     * 校验目标节点是否是第一个节点（非开始节点）
     *
     * @param targetKey 目标节点
     * @param bpmnModel bpmnModel
     * @return
     */
    public static boolean checkFirstTaskNode(String targetKey, BpmnModel bpmnModel) {
        Process mainProcess = bpmnModel.getMainProcess();
        // 获取开始节点
        List<StartEvent> elements = mainProcess.findFlowElementsOfType(StartEvent.class, false);
        StartEvent startEvent = elements.get(0);
        return mainProcess.getFlowElements().stream()
                .filter(item -> item instanceof SequenceFlow)
                .anyMatch(item ->{
                    SequenceFlow sequenceFlow = (SequenceFlow) item;
                    return sequenceFlow.getSourceRef().equals(startEvent.getId()) && sequenceFlow.getTargetRef().equals(targetKey);
                });
    }


    /**
     * 获取目标节点的FlowElement
     *
     * @param targetKey 目标节点
     * @param bpmnModel bpmnModel
     * @return
     */
    public static FlowElement getFlowElement(String targetKey, BpmnModel bpmnModel) {
        Process mainProcess = bpmnModel.getMainProcess();
        return mainProcess.getFlowElement(targetKey);
    }

    /**
     * 校验目标节点和当前节点是否存在链接先
     *
     * @param sourceElementKey 当前节点
     * @param targetElementKey 目标节点
     * @param processes        所有节点信息
     * @return
     */
    public static boolean checkCurrentToTargetElement(String sourceElementKey, String targetElementKey, List<Process> processes) {
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (CollectionUtil.isEmpty(flowElements)) {
                continue;
            }
            // 流程线
            List<SequenceFlow> sequenceFlows = flowElements.stream().filter(e -> e instanceof SequenceFlow).map(e -> (SequenceFlow) e).collect(Collectors.toList());
            //获取起始点出发的线
            List<SequenceFlow> sourceSequenceFlows = sequenceFlows.stream().collect(Collectors.groupingBy(SequenceFlow::getSourceRef)).get(sourceElementKey);
            List<String> targetRefs = sourceSequenceFlows.stream().map(SequenceFlow::getTargetRef).collect(Collectors.toList());
            //目标节点
            if (targetRefs.contains(targetElementKey)) {
                return Boolean.TRUE;
            }
            //获取目标点连接进来的线
            List<SequenceFlow> targetSequenceFlows = sequenceFlows.stream().collect(Collectors.groupingBy(SequenceFlow::getTargetRef)).get(targetElementKey);
            List<String> sourceRefs = targetSequenceFlows.stream().map(SequenceFlow::getSourceRef).collect(Collectors.toList());
            if (sourceRefs.contains(sourceElementKey)) {
                return Boolean.TRUE;
            }
            //存在两个集合的交集也就存在链接线
            Collection<String> intersection = CollectionUtils.intersection(targetRefs, sourceRefs);
            if (!intersection.isEmpty()) {
                return Boolean.TRUE;
            }
        }
        return false;
    }

    /**
     * 校验流程线表达式是否成立
     *
     * @param condition 表达式
     * @param variables 参数
     */
    public static boolean checkSequenceCondition(String condition, Map<String, Object> variables) {
        if (StringUtils.isBlank(condition)) {
            return true;
        }
        if (variables.isEmpty()) {
            return false;
        }
        boolean isChar = false;
        if (condition.contains("==")) {
            isChar = true;
        }
        for (String key : variables.keySet()) {
            if (isChar) {
                condition = condition.replace(key, StringUtils.appendChar(MapUtil.get(variables, key, String.class), "\'"));
            } else {
                condition = condition.replace(key, MapUtil.get(variables, key, String.class));
            }
        }
        try {
            SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
            TemplateParserContext templateParserContext = new TemplateParserContext("${", "}");
            Expression expression = spelExpressionParser.parseExpression(condition, templateParserContext);
            Object value = expression.getValue();
            if (value == null) {
                return false;
            }
            return (boolean) value;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 查找下一节点
     *
     * @param currentNodeKey 当前节点key
     * @param bpmnModel      流程图
     * @param flowType       流程类型
     * @param variables      流程参数
     * @return
     */
    public static List<FlowElement> findNextElements(String currentNodeKey, BpmnModel bpmnModel, String flowType, Map<String, Object> variables) {
        List<FlowElement> flowElements = new ArrayList<>();
        //所有流程图信息
        List<Process> processes = bpmnModel.getProcesses();
        //所有流程节点
        if (CollectionUtil.isEmpty(processes)) {
            return flowElements;
        }
        for (Process process : processes) {
            Collection<FlowElement> elements = process.getFlowElements();
            if (CollectionUtil.isEmpty(elements)) {
                continue;
            }
            flowElements.addAll(findNormalNextNode(currentNodeKey, elements, variables, flowType));
        }
        return flowElements;
    }

    /**
     * 获取正常提交流程的下一节点
     *
     * @param currentNodeKey 当前节点
     * @param elements       所有流程节点
     * @param variables      流程参数
     * @return
     */
    private static List<FlowElement> findNormalNextNode(String currentNodeKey, Collection<FlowElement> elements, Map<String, Object> variables, String flowType) {
        List<FlowElement> returnElements = new ArrayList<>();
        //获取所有流程线
        List<SequenceFlow> sequenceFlowList = elements.stream()
                .filter(item -> item instanceof SequenceFlow)
                .map(item -> (SequenceFlow) item)
                .collect(Collectors.toList());
        //当前节点对应的下一节点是用户任务
        List<FlowElement> conditionElements = getConditionElement(currentNodeKey, variables, sequenceFlowList, flowType);
        if (!conditionElements.isEmpty()) {
            returnElements.addAll(conditionElements);
        }

        //下一节点不是用户任务而是互斥网关
        List<String> targetRefs = sequenceFlowList.stream()
                //源接节点是当前节点
                .filter(item -> item.getSourceRef().equals(currentNodeKey))
                //下一节点是用户任务
                .filter(item -> item.getTargetFlowElement() instanceof ExclusiveGateway)
                .map(SequenceFlow::getTargetRef)
                .collect(Collectors.toList());
        //根据互斥网关的节点查找对应的下一节点
        for (String targetRef : targetRefs) {
            //当前节点对应的下一节点是用户任务
            List<FlowElement> conditionElement = getConditionElement(targetRef, variables, sequenceFlowList, flowType);
            if (!conditionElement.isEmpty()) {
                returnElements.addAll(conditionElement);
            }
        }

        return returnElements;
    }

    /**
     * 获取满足条件的Element
     *
     * @param currentNodeKey   目标节点key
     * @param variables        流程参数
     * @param sequenceFlowList 流程线
     */
    private static List<FlowElement> getConditionElement(String currentNodeKey, Map<String, Object> variables, List<SequenceFlow> sequenceFlowList, String flowType) {
        //驳回
        if (WorkflowConstants.TASK_TYPE_REJECT.equals(flowType)) {
            //只将驳回标识参数放入map
            Map<String, Object> map = new HashMap<>();
            return sequenceFlowList.stream()
                    //源接节点是当前节点
                    .filter(item -> item.getSourceRef().equals(currentNodeKey))
                    //下一节点是用户任务
                    .filter(item -> item.getTargetFlowElement() instanceof UserTask)
                    //驳回线
                    .filter(item -> StringUtils.containsIgnoreCase(item.getId(), WorkflowConstants.SEQUENCE_FLOW_TYPE_REJECT))
                    //判断表达式是否满足
                    .filter(item -> checkSequenceCondition(item.getConditionExpression(), map))
                    .map(SequenceFlow::getTargetFlowElement)
                    .collect(Collectors.toList());

        } else {
            return sequenceFlowList.stream()
                    //源接节点是当前节点
                    .filter(item -> item.getSourceRef().equals(currentNodeKey))
                    //下一节点是用户任务或者结束
                    .filter(item -> item.getTargetFlowElement() instanceof UserTask || item.getTargetFlowElement() instanceof EndEvent)
                    //不是驳回线
                    .filter(item -> !StringUtils.containsIgnoreCase(item.getId(), WorkflowConstants.SEQUENCE_FLOW_TYPE_REJECT))
                    //判断表达式是否满足
                    .filter(item -> checkSequenceCondition(item.getConditionExpression(), variables))
                    .map(SequenceFlow::getTargetFlowElement)
                    .collect(Collectors.toList());
        }

    }

    public static void getHighLightNodeAndFlow(BpmnModel bpmnModel,
                                               List<HistoricActivityInstance> historicActivityInstances,
                                               List<String> highLightedFlows,
                                               List<String> highLightedNodes) {
        //获取历史任务节点
        List<String> taskNodes = historicActivityInstances.stream().filter(item -> !"sequenceFlow".equals(item.getActivityType())).map(item -> item.getActivityId()).collect(Collectors.toList());
        highLightedNodes.addAll(taskNodes);
        List<String> sequeneces = historicActivityInstances.stream().filter(item -> "sequenceFlow".equals(item.getActivityType())).map(item -> item.getActivityId()).collect(Collectors.toList());
        highLightedFlows.addAll(sequeneces);


        //获取所有流程图节点
//        List<Process> processes = bpmnModel.getProcesses();
//        for (Process process : processes) {
//            Collection<FlowElement> flowElements = process.getFlowElements();
//            if (CollectionUtil.isEmpty(flowElements)) {
//                continue;
//            }
//            //获取所有线
//            List<SequenceFlow> sequenceFlows = flowElements.stream().filter(item -> item instanceof SequenceFlow).map(item -> (SequenceFlow) item).collect(Collectors.toList());
//            for (SequenceFlow sequenceFlow : sequenceFlows) {
//                if (CollectionUtil.contains(highLightedNodes,sequenceFlow.getSourceRef())
//                        && CollectionUtil.contains(highLightedNodes,sequenceFlow.getTargetRef())
//                        && !CollectionUtil.contains(highLightedFlows,sequenceFlow.getId())) {
//                    highLightedFlows.add(sequenceFlow.getId());
//                }
//            }
//        }
    }

    /**
     * 获取当前节点与目标节点之间的流程线
     *
     * @param bpmnModel
     * @param taskDefinitionKey
     * @param targetKey
     * @return
     */
    public static List<FlowElement> getSequenceFlowKey(BpmnModel bpmnModel, String taskDefinitionKey, String targetKey) {
        List<FlowElement> flowElementList = new ArrayList<>();
        List<Process> processes = bpmnModel.getProcesses();
        for (Process process : processes) {
            // 所有的节点
            Collection<FlowElement> flowElements = process.getFlowElements();
            //目标节点等于当前节点直接返回
            if(taskDefinitionKey.equals(targetKey)){
                flowElementList.addAll(flowElements.stream().filter(item -> item.getId().equals(targetKey)).collect(Collectors.toList()));
                break;
            }
            // 获取所有的连线
            List<SequenceFlow> sequenceFlows = flowElements.stream().filter(item -> item instanceof SequenceFlow).map(item -> (SequenceFlow) item).collect(Collectors.toList());
            // 获取目标节点的任务
            List<FlowElement> flowElements1 = sequenceFlows.stream()
                    .filter(item -> item.getSourceRef().equals(taskDefinitionKey) && item.getTargetRef().equals(targetKey))
                    .map(SequenceFlow::getTargetFlowElement).collect(Collectors.toList());
            if (!flowElements1.isEmpty()) {
                flowElementList.addAll(flowElements1);
            }
            //获取当前节点开始的所有目标节点信息
            List<String> SourceTargetKeys = sequenceFlows.stream().filter(item -> item.getSourceRef().equals(taskDefinitionKey))
                    .map(SequenceFlow::getTargetRef).collect(Collectors.toList());
            //获取目标节点结束的源节点信息
            List<String> tarSourceKeys = sequenceFlows.stream().filter(item -> item.getTargetRef().equals(targetKey))
                    .map(SequenceFlow::getSourceRef).collect(Collectors.toList());
            //两个节点信息的交集（两个节点中间的节点信息）
            Collection<String> commonKeys = CollectionUtils.intersection(SourceTargetKeys, tarSourceKeys);
            if (commonKeys.isEmpty()) {
                List<SequenceFlow> flowList = sequenceFlows.stream().filter(item -> item.getSourceRef().equals(taskDefinitionKey) && item.getTargetRef().equals(targetKey)).collect(Collectors.toList());
                flowElementList.addAll(flowList);
                continue;
            }
            //获取当前节点与中间节点的所有节点信息
            // 当前节点与交集节点之间的节点
            List<SequenceFlow> sequenceFlows1 = sequenceFlows.stream()
                    .filter(item -> item.getSourceRef().equals(taskDefinitionKey) && CollectionUtil.contains(commonKeys, item.getTargetRef()))
                    .collect(Collectors.toList());
            if (!sequenceFlows1.isEmpty()) {
                flowElementList.addAll(sequenceFlows1);
            }
            // 目标接与交集节点之间的节点
            List<SequenceFlow> sequenceFlows2 = sequenceFlows.stream()
                    .filter(item -> item.getTargetRef().equals(targetKey) && CollectionUtil.contains(commonKeys, item.getSourceRef()))
                    .collect(Collectors.toList());
            if (!sequenceFlows2.isEmpty()) {
                flowElementList.addAll(sequenceFlows2);
            }
            //获取交集交集节点信息
            List<FlowElement> sequenceFlows3 = flowElements.stream().filter(item -> CollectionUtil.contains(commonKeys, item.getId())).collect(Collectors.toList());
            if(!sequenceFlows3.isEmpty()){
                flowElementList.addAll(sequenceFlows3);
            }
        }
        return flowElementList;
    }

    /**
     * 判断是否委托任务
     *
     * @param task 任务信息
     * @return
     */
    public static boolean delegateTask(Task task,Map<String, Object> variables) {
        return WorkflowConstants.TASK_TYPE_DELEGATE.equalsIgnoreCase(MapUtil.get(variables, WorkflowConstants.TASK_SUBMIT_TYPE, String.class));
    }

    /**
     * 判断会签是否结束
     *
     * @param flowElement 当前任务节点
     * @param variables   当前任务变量
     * @return
     */
    public static boolean checkMultiComplete(FlowElement flowElement, Map<String, Object> variables) {
        boolean isMultiInstance = false;
        if (flowElement instanceof UserTask) {
            UserTask userTask = (UserTask) flowElement;
            isMultiInstance = userTask.hasMultiInstanceLoopCharacteristics();
        }
        //获取任务变量
        if (isMultiInstance) {
            //会签节点要所有的实例都要完成
            //完成的实例数,加上本次的任务
            Integer completeNum = MapUtil.getInt(variables, WorkflowConstants.MULTI_INSTANCE_COMPLETED, 0) + 1;
            //总的是实例数
            Integer totalNum = MapUtil.getInt(variables, WorkflowConstants.MULTI_INSTANCE_TOTAL, 0);
            if (totalNum == 0) {
                throw new WorkflowException("任务处理失败:会签实例总数为0!");
            }
            //所有实例完成后归档
            if (completeNum / totalNum == 1) {
                return true;
            } else {
                return false;
            }
        }
        return true;
    }
}
